package com.amkai.aes.webservice.binary.objecthandler.billing;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;

import com.amkai.aes.aescommon.billing.InsuranceBillingUtils;
import com.amkai.aes.aescommon.exceptions.AOException;
import com.amkai.aes.aescommon.factory.RADBeanFactory;
import com.amkai.aes.aescommon.service.binary.job.billing.InsuranceBillingJob;
import com.amkai.aes.aescommon.service.result.billing.InsuranceBillingResult;
import com.amkai.aes.generatedbeans.bill.BillCharge;
import com.amkai.aes.generatedbeans.bill.BillStatusKind;
import com.amkai.aes.generatedbeans.bill.InsuranceBill;
import com.amkai.aes.generatedbeans.billingledger.Charge;
import com.amkai.aes.generatedbeans.billingledger.details.FromToKind;
import com.amkai.aes.generatedbeans.organization.Department;
import com.amkai.aes.generatedbeans.passport.Passport;
import com.amkai.aes.server.session.ServiceSessionManager;
import com.amkai.aes.webservice.binary.JobObjectHandler;
import com.amkai.common.base.utils.StringUtils;
import com.amkai.common.extensions.hibernate.config.SiteManager;
import com.amkai.common.extensions.hibernate.config.manager.SessionManager;

public class InsuranceBillingHandler implements	JobObjectHandler<InsuranceBillingJob, Department, InsuranceBillingResult> {

	private static final String TITLE = "Insurance Billing (Server)";
	private static final Log log = LogFactory.getLog(InsuranceBillingHandler.class);
	
	@Override
	public InsuranceBillingResult process(InsuranceBillingJob job, Department dep) throws Exception {
		switch (job.getKind()) {
		case REFRESH_GROUP_BILLING:	return refreshGroupBilling(job, dep);
		case MARK_CHARGES_BILLED:	return markChargesBilled(job);
		}
		throw new AOException(TITLE, "Unknown JobKind", job.getKind().toString());
	}
	
	protected InsuranceBillingResult refreshGroupBilling(InsuranceBillingJob job, Department moduleDepartment) throws Exception {
		long start = System.currentTimeMillis();
		InsuranceBillingResult result = new InsuranceBillingResult();
		SessionManager sm = SiteManager.Start();
		try {
			StringBuilder s = new StringBuilder("FROM Charge ch " +
					"WHERE ch.generateClaim = 1 " +
					"AND ch.currentBillCharge IS NULL " + 
					"AND ch.isActive = 1 " +
					"AND ch.batch.period.baseOrganization = :bo "+ 
					"AND ch.fromEntry.kindWrapper.id IN (:pi,:si,:ti) " + 
					// KKORNEL: AES-16888
					"ORDER BY ch.transactionDate ASC, ch.id ASC");
			
			Query query = sm.execute(s.toString())
				.setParameter("bo", moduleDepartment)
				.setParameter("pi", FromToKind.PRIMARY_INSURANCE.ordinal())
				.setParameter("si", FromToKind.SECONDARY_INSURANCE.ordinal())
				.setParameter("ti", FromToKind.TERTIARY_INSURANCE.ordinal());
			
			List<Charge> l = query.list();
			
			log.debug(String.format("Charge query runs in %d msec", System.currentTimeMillis()-start));
			start = System.currentTimeMillis();
			log.debug(String.format("Found %d charge(s)", l.size()));
			result.setPayors(InsuranceBillingUtils.createInsuranceBillingPayorsTableRows(l));
			log.debug(String.format("Charge processing runs in %d msec", System.currentTimeMillis()-start));
		}
		catch (Exception ex) {
			result.setException(ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
/*	
	private String getFormOutput(InsuranceCarrier ic, InsuranceBillingOutputKind outputKind) throws Exception {
		
		String PAPER_OUTPUT_TEMPLATE = "Paper (%s)";

		if (ic.getEmcModule() != null) {
			ModuleKind mk = ic.getEmcModule().getModuleKind();
			if (mk != null) {
				switch (mk) {
				case X12_INSTITUTIONAL:
				case X12_PROFESSIONAL:
					switch (outputKind) {
					case PAPER:
					case DOCUMENT_NEEDED:
						return String.format(PAPER_OUTPUT_TEMPLATE, getFormOutput(ic));
					default:
						return "X12";
					}
				case PI_HCFA:
				case PI_UB:
					switch (outputKind) {
					case PAPER:
					case DOCUMENT_NEEDED:
						return String.format(PAPER_OUTPUT_TEMPLATE, getFormOutput(ic));
					default:
						return String.format(PAPER_OUTPUT_TEMPLATE, mk.toString());
					}
				}
			}
			else
				return String.format(PAPER_OUTPUT_TEMPLATE, getFormOutput(ic));
		}
		else
			return String.format(PAPER_OUTPUT_TEMPLATE, getFormOutput(ic));
		throw new AOException(TITLE, "Unknown Form Output", "Unknown Form Output");
	}
	
	private String getFormOutput(InsuranceCarrier ic) throws Exception {
		if (ic.getClaimForm() != null)
			return ic.getClaimForm().getName();
		throw new AOException(TITLE, "Claim Form is not set", String.format("Claim Form is not set for %s", ic.getName()));
	}
*/	

	private InsuranceBillingResult markChargesBilled(InsuranceBillingJob job) throws Exception {
		SessionManager sm = SiteManager.Start();
		InsuranceBillingResult result = new InsuranceBillingResult();
		try {
			sm.openTransaction();
			
			Passport passport = sm.load(Passport.class, ServiceSessionManager.getConnection().getRuntimePassport().getId());
			
			InsuranceBill insuranceBill = RADBeanFactory.getInstance().createBean(InsuranceBill.class);
			insuranceBill.setCreated(new Date());
			insuranceBill.setBaseOrganization(job.getModuleDepartment());
			insuranceBill.setCreator(passport);
			insuranceBill.setEmcKind(job.getEmcKind());
			insuranceBill.setFormatKind(job.getBillFormatKind());
			//AES-16992
			//insuranceBill.setBilled(true);
			insuranceBill.setBillStatus(BillStatusKind.BILLED);
			
			sm.save(insuranceBill);
			
			List<String> errors = new ArrayList<String>();
			log.debug(String.format("Mark %d Charge(s) as billed", job.getIDList().size()));
			int c = 0;
			for (Long id : job.getIDList()) {
				Charge charge = sm.get(Charge.class, id);
				if (charge.getCurrentBillCharge() != null)
					errors.add(String.format("BT#%d Account: %s", charge.getId(), charge.getAccount().getAccountNumber()));
				BillCharge billCharge = RADBeanFactory.getInstance().createBean(BillCharge.class);
				billCharge.setCharge(charge);
				billCharge.setBill(insuranceBill);
				
				billCharge.setChargedToAmount(charge.getDue());
				billCharge.setChargedToKind(charge.getFromEntry().getKind());
				switch (billCharge.getChargedToKind()) {
				case PRIMARY_INSURANCE:
				case SECONDARY_INSURANCE:
				case TERTIARY_INSURANCE:
					billCharge.setChargedToInsuranceClaimOffice(charge.getFromEntry().getInsurancePolicy().getInsuranceClaimOffice());
					break;
				}
				
				sm.save(billCharge);
				charge.setCurrentBillCharge(billCharge);
				sm.save(charge);
				c++;
			}
			result.setBilledChargesCount(c);
			if (errors.size() > 0) {
				throw new AOException(TITLE, "Billed Charges", "Some of the Charges are billed!", StringUtils.commaText(errors, "\n", null));
			}
			log.debug(String.format("%d Charge(s) was/were marked as billed", c));
			sm.commitTransaction();
		}
		catch (Exception ex) {
			sm.rollbackTransaction();
			result.setException(ex);
			log.error(TITLE, ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
}
